#include <iostream>
#include <signal.h>
#include <wfrest/HttpServer.h>
#include <wfrest/Json.h>
#include <workflow/WFFacilities.h>
#include <workflow/WFTaskFactory.h>
#include <workflow/MySQLResult.h>
#include <openssl/sha.h>
#include <random>
#include <sstream>
#include <workflow/MySQLResult.h>
#include <jwt-cpp/jwt.h>

using std::cerr;
using std::cout;
using std::istringstream;
using std::map;
using std::string;
using std::vector;

WFFacilities::WaitGroup waitGroup{1};
string secret = "hashcode_wtt";

struct LoginContext
{
    const wfrest::HttpReq *req;
    wfrest::HttpResp *resp;
};

void signalHandler(int)
{
    waitGroup.done();
}

// 获取随机的盐值
string getSaltValue(size_t len = 16)
{
    string salt;
    static const char charset[] =
        "0123456789"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "abcdefghijklmnopqrstuvwxyz";
    std::random_device rd;
    std::mt19937 gen(rd());
    std::uniform_int_distribution<> dist(0, sizeof(charset) - 2);

    for (size_t i = 0; i < len; i++)
    {
        salt += charset[dist(gen)];
    }
    return salt;
}

// 生成密文
string sha256(const string input)
{
    unsigned char hash[SHA256_DIGEST_LENGTH];
    SHA256((const unsigned char *)input.c_str(), input.size(), hash);

    std::stringstream ss;
    for (int i = 0; i < SHA256_DIGEST_LENGTH; i++)
    {
        ss << std::hex << std::setw(2) << std::setfill('0') << (int)hash[i];
    }
    return ss.str();
}

// 生成token
std::string generate_token(const std::string &username, const std::string &secret)
{
    auto token = jwt::create()
                     .set_issuer("auth-server")                                                  // 可选：设置签发者
                     .set_type("JWS")                                                            // 可选
                     .set_payload_claim("username", jwt::claim(username))                        // 设置载荷
                     .set_expires_at(std::chrono::system_clock::now() + std::chrono::minutes(2)) // 有效期
                     .sign(jwt::algorithm::hs256{secret});                                       // 使用 HS256 算法和密钥签名

    auto now = std::chrono::system_clock::now();
    std::time_t now_c = std::chrono::system_clock::to_time_t(now);
    std::cerr << std::ctime(&now_c);

    return token;
}

// 验证token
bool verify_token(const std::string &token, const std::string &secret, std::string &username_out)
{
    try
    {
        auto decoded = jwt::decode(token);
        auto verifier = jwt::verify()
                            .allow_algorithm(jwt::algorithm::hs256{secret})
                            .with_issuer("auth-server");

        verifier.verify(decoded);

        // 获取 username
        auto claim = decoded.get_payload_claim("username");
        username_out = claim.as_string();
        return true;
    }
    catch (const std::exception &e)
    {
        std::cerr << "Token verification failed: " << e.what() << std::endl;
        return false;
    }
}

//
void writeLoginMessToRedis(WFRedisTask *task)
{
}

void loginMysqlCallback(WFMySQLTask *mysqlTask)
{
    // 状态信息和错误码
    int state = mysqlTask->get_state();
    int error = mysqlTask->get_error();
    // 获取序列
    SeriesWork *series = series_of(mysqlTask);
    // 获取上下文
    LoginContext *loginContext = static_cast<LoginContext *>(series->get_context());
    // 处理状态信息
    if (state != WFT_STATE_SUCCESS)
    {
        cerr << WFGlobal::get_error_string(state, error) << "\n";
        loginContext->resp->set_status_code("500");
        return;
    }
    // 获取数据库查询结果
    wfrest::Json resJson;
    protocol::MySQLResultCursor cursor(mysqlTask->get_resp());

    // 获取客户端传过来的json
    wfrest::Json reqJson = loginContext->req->json();
    if (cursor.get_rows_count() != 0)
    {
        // 获取查询到的盐值和密文
        vector<protocol::MySQLCell> row;
        cursor.fetch_row(row);
        string salt = row[0].as_string();
        string ciphertext = row[1].as_string();

        // 根据盐值算密码是否正确
        if (ciphertext == sha256(salt + reqJson["password"].get<string>()))
        {
            // 登录成功存在
            // 生成token
            string token = generate_token(reqJson["username"].get<string>(), secret);

            resJson["success"] = true;
            resJson["message"] = "Login success";
            resJson["token"] = token;
            loginContext->resp->Json(resJson);
            // // 将该用户名和盐值写入redis
            // WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 3, nullptr);
            // protocol::RedisRequest *redisReq = redisTask->get_req();
            // redisReq->set_request("HSET", {"login", reqJson["username"].get<string>(), salt});
            // // 添加到序列
            // series->push_back(redisTask);
        }
        else
        {
            // 密码错误
            resJson["success"] = false;
            resJson["message"] = "incorrect password";
            loginContext->resp->Json(resJson);
            return;
        }
    }
    else
    {
        // 该用户不存在
        resJson["success"] = false;
        resJson["message"] = "Username is not exist";
        loginContext->resp->Json(resJson);
        return;
    }
}

void loginRedisCallback(WFRedisTask *task)
{
    // 获取任务执行结果的状态
    int state = task->get_state();
    int error = task->get_error();
    // 获取上下文
    LoginContext *loginContext = static_cast<LoginContext *>(series_of(task)->get_context());
    // 获取发送过来的json
    wfrest::Json reqJson = loginContext->req->json();
    if (state != WFT_STATE_SUCCESS)
    {
        cerr << "error: " << WFGlobal::get_error_string(state, error) << "\n";
        return;
    }
    // 获取redis查询结果,黑名单查看
    protocol::RedisResponse *redisResp = task->get_resp();
    protocol::RedisValue val;
    redisResp->get_result(val);
    // 处理结果，返回响应
    wfrest::Json resJson;
    if (val.is_nil())
    {
        // 查看是否捎带token
        string token = loginContext->req->header("Authorization");
        cout << token << "\n";

        if (!token.empty())
        {
            // 验证token
            string username;
            verify_token(token, secret, username);
            if (reqJson["username"].get<string>() == username)
            {
                // 验证通过,回复响应
                resJson["success"] = true;
                resJson["message"] = "token verify success";
                loginContext->resp->Json(resJson);
                return;
            } // token失效，查表
        }
        // redis中没有，需要查询数据库

        // 获取序列
        SeriesWork *series = series_of(task);
        // 创建任务
        WFMySQLTask *mysqlTask = WFTaskFactory::create_mysql_task("mysql://root:123@127.0.0.1:3306/netdisk_v3", 3, loginMysqlCallback);
        protocol::MySQLRequest *req = static_cast<protocol::MySQLRequest *>(mysqlTask->get_req());

        // sql语句的拼接
        string sql = "select salt,password from user_info  where username = ";
        sql += "'" + loginContext->req->json()["username"].get<string>() + "'";
        req->set_query(sql);
        series->push_back(mysqlTask);
        return;
    }
    else if (val.is_string())
    {
        // // redis中有，直接算
        // if (loginContext->req->json()["password"].get<string>() == val.string_value())
        // {
        //     resJson["success"] = true;
        //     resJson["message"] = "Login success";
        //     loginContext->resp->Json(resJson);
        // }
        // else
        // {
        //     resJson["success"] = false;
        //     resJson["message"] = "Login fail. Incorrect password";
        //     loginContext->resp->Json(resJson);
        // }
    }
}

void registerAddUser(WFMySQLTask *task)
{
    int state = task->get_state();
    int error = task->get_error();

    // 获取当前所在序列
    SeriesWork *series = series_of(task);
    // 获取上下文
    LoginContext *loginContext = static_cast<LoginContext *>(series->get_context());

    if (state != WFT_STATE_SUCCESS)
    {
        cerr << WFGlobal::get_error_string(state, error) << "\n";
        loginContext->resp->set_status_code("500");

        wfrest::Json respJson;
        respJson["success"] = false;
        respJson["message"] = "Database error during registration";
        loginContext->resp->Json(respJson);

        return;
    }

    // 可选：可以检查 SQL 执行是否影响了预期的行数
    auto *resp = task->get_resp();
    if (resp->get_affected_rows() == 0)
    {
        loginContext->resp->set_status_code("400");

        wfrest::Json respJson;
        respJson["success"] = false;
        respJson["message"] = "No rows affected; registration may have failed";
        loginContext->resp->Json(respJson);
        return;
    }

    // 响应成功,生成token并返回
    string token = generate_token(loginContext->req->json()["username"], secret);
    wfrest::Json respJson;
    respJson["success"] = true;
    respJson["message"] = "regiset success";
    respJson["token"] = token;
    loginContext->resp->Json(respJson);
    return;
}

void registerMysqlCallback(WFMySQLTask *task)
{
    int state = task->get_state();
    int error = task->get_error();
    // 获取序列
    SeriesWork *series = series_of(task);
    // 获取上下文
    LoginContext *loginContext = static_cast<LoginContext *>(series->get_context());
    if (state != WFT_STATE_SUCCESS)
    {
        cerr << WFGlobal::get_error_string(state, error) << "\n";
        loginContext->resp->set_status_code("500");
        return;
    }
    protocol::MySQLResponse *mysqlResp = task->get_resp();
    protocol::MySQLResultCursor cur{mysqlResp};

    wfrest::Json ret;

    if (cur.get_rows_count() != 0)
    {
        ret["success"] = false;
        ret["message"] = "The user already exist.";
        loginContext->resp->Json(ret);
        return;
    }

    // 通过上下文获取接收到json
    wfrest::Json reqJson = loginContext->req->json();

    // 用户不存在，注册
    WFMySQLTask *newMysqlTask = WFTaskFactory::create_mysql_task("mysql://root:123@127.0.0.1:3306/netdisk_v3", 4, registerAddUser);

    // 生成随机盐值
    string salt = getSaltValue();
    // 生成密文
    string ciphertext = sha256(salt + reqJson["password"].get<string>());

    // sql语句
    std::stringstream ss;
    ss << "insert into user_info (username, salt, password) values( '"
       << reqJson["username"].get<string>() << "','" << salt << "','" << ciphertext << "');";

    // 加入序列中
    cout << ss.str() << "\n";
    newMysqlTask->get_req()->set_query(ss.str());
    series->push_back(newMysqlTask);
}

int main()
{

    signal(SIGINT, signalHandler);

    wfrest::HttpServer server;

    server.GET("/index.html", [](const wfrest::HttpReq *req, wfrest::HttpResp *resp, SeriesWork *series)
               { resp->File("index.html"); });

    server.GET("/welcome.html", [](const wfrest::HttpReq *req, wfrest::HttpResp *resp, SeriesWork *series)
               { resp->File("welcome.html"); });

    server.GET("/register.html", [](const wfrest::HttpReq *req, wfrest::HttpResp *resp, SeriesWork *series)
               { resp->File("register.html"); });

    server.POST("/login", [](const wfrest::HttpReq *req, wfrest::HttpResp *resp, SeriesWork *series)
                {
                    wfrest::Json reqJson = req->json();
                    cout << reqJson["username"] << ": " << reqJson["password"] << "\n";

                    // 添加上下文
                    LoginContext *loginContext = new LoginContext();
                    loginContext->req = req;
                    loginContext->resp = resp;
                    series->set_context(loginContext);

                    // redis任务
                    WFRedisTask *redisTask = WFTaskFactory::create_redis_task("redis://127.0.0.1:6379", 3, loginRedisCallback);
                    redisTask->get_req()->set_request("HGET", {"login", reqJson["username"].get<string>()});
                    // 将任务添加到序列中
                    series->push_back(redisTask); });

    server.POST("/register", [](const wfrest::HttpReq *req, wfrest::HttpResp *resp, SeriesWork *series)
                {
                    wfrest::Json reqJson = req->json();
                    cout << reqJson["username"] << ": " << reqJson["password"] << "\n";

                    // 添加上下文
                    LoginContext* loginContext = new LoginContext();
                    loginContext->req = req;
                    loginContext->resp = resp;
                    series->set_context(loginContext);

                    // 查数据库
                    WFMySQLTask* mysqlTask = WFTaskFactory::create_mysql_task("mysql://root:123@127.0.0.1:3306/netdisk_v3", 4, registerMysqlCallback);
                    protocol::MySQLRequest * mysqlReq = mysqlTask->get_req();
                    string sql = "select salt from user_info where username = ";
                    sql += "'" + reqJson["username"].get<string>() + "';";
                    mysqlReq->set_query(sql); 
                    series->push_back(mysqlTask); });

    if (server.track().start(9898) == 0)
    {
        server.list_routes();
        waitGroup.wait();
        server.stop();
        cerr << "server over" << "\n";
    }
    else
    {
        cerr << "server error" << "\n";
    }
    return 0;
}